package org.opens.lambda.parallel;

import org.junit.Test;

import java.util.concurrent.ForkJoinPool;

/**
 * 简介:
 *      Fork Join框架总体的理念就是分而治之, 就是把一个任务分解成多个任务执行, 最后再将这些任务执行的结果合并起来.
 * 实例:
 *      1. 例如: 现在要计算1, 2, 3, 4, 5, 6, 7, 8, 9, 10这10个数的和.
 *          - Fork Join以一定的粒度去分解任务, 例如一个任务计算的个数小于等于3个才会进行计算, 则这个计算任务会被分解为:
 *              1. 首先, 均分这个数组: [1, 2, 3, 4, 5], [6, 7, 8, 9, 10]两个任务, 检测发现么个任务的计算个数仍旧大于3, 继续分解任务:
 *              2. 继续均分分解完的任务: [1, 2, 3], [4, 5]; [6, 7, 8], [9, 10]; 检测到每个任务计算的个数都小于等于3了, 开始计算;
 *              3. 计算得出结果: [1, 2, 3]线程计算出结果6, [4, 5]线程计算出结果9, [6, 7, 8]线程计算出结果21, [9, 10]线程计算出结果19, 然后Fork Join框架会将每个线程计算的结果进行合并
 *              4. 合并规则为: [1, 2, 3]线程结果和[4, 5]线程结果合并到[1, 2, 3, 4, 5]线程; [6, 7, 8]线程结果和[9, 10]线程线程结果合并到[6, 7, 8, 9, 10]线程, 未到达主线程, 继续合并:
 *              5. [1, 2, 3, 4, 5]线程结果和[6, 7, 8, 9, 10]线程结果合并到主线程, 主线程返回值.
 *          - 在上面的过程中, 分任务的过程就是fork的过程, 而每个子线程将结果返回给父线程进行合并结果集的过程为join过程.
 *
 *
 */
public class ForkJoinDemo {

    private static int[] data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    @Test
    public void test1() {
        System.out.println(calc1(data));

        AccmulatorRecursiveTask task = new AccmulatorRecursiveTask(0, data.length, data);
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        Integer invoke = forkJoinPool.invoke(task);
        System.out.println(invoke);
    }

    private static int calc1(int[] arr) {
        int result = 0;
        for (int i : arr) {
            result += i;
        }
        return result;
    }

    /**
     * 说明:
     *      1. 使用静态内部类来实现容纳结果集会造成线程不安全, 更好的做法是创建内部类对象来存储结果集.
     * 输出:
     *      0
     *      55
     *      55
     */
    @Test
    public void test2() {
        AccmulatorRecursiveAction action = new AccmulatorRecursiveAction(0, data.length, data);
        AccmulatorRecursiveAction action1 = new AccmulatorRecursiveAction(0, data.length, data);
        System.out.println(action.getResult());
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        forkJoinPool.invoke(action);
        System.out.println(action.getResult());
        System.out.println(action1.getResult());
    }

}
